import json
from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync  # 同步(从同步调异步:如：Django视图调用Channels层)
from asgiref.sync import sync_to_async  # 同步(从异步调同步:如：WebSocket消费者调用Django ORM)
# from django_redis import get_redis_connection  # django的redis数据库连接,普通类型
from django.core.cache import cache  # django的redis数据库连接, 字典类型的
from channels.layers import get_channel_layer

from utils.report import alibaba as pus_ali
from utils.report import sentDevice

from utils.subscribe import alibaba as sub_ali
from app01.models import DeviceData, Device, FunDefine
from django.http import JsonResponse
from collections import defaultdict


class ChannelLayerManager:
    def __init__(self):
        self.channel_layer = get_channel_layer()  # 做为共享组用


# 跟阿里物联网实时连接
class ChatConsumer(WebsocketConsumer):
    # 创建共享队列
    channel_layer_manager = ChannelLayerManager()

    def websocket_connect(self, message):
        # 有客户端来向服务器端发送websocket连接的请求时，自动触发
        group_device_name = self.scope['url_route']['kwargs']['group']
        # 服务端允许和客户端创建连接
        self.accept()

        print(group_device_name + '连接成功')
        # 把自己加到group_device_name这个组的队列中
        async_to_sync(self.channel_layer_manager.channel_layer.group_add)(
            group_device_name,
            self.channel_name,
        )
        # 调用阿里云物联网平台,如果要用其他平台,在这里加上判断就行
        dict_iot = cache.get('dict_iot')  # 根据dict_iot去获取dict_iot,用的是django-redis
        # 订阅阿里物联网平台服务器
        sub_ali.stomp_amqp(dict_iot, self.channel_layer_manager)

    def websocket_receive(self, message):
        group_device_name = self.scope['url_route']['kwargs']['group']
        # 浏览器基于websocket向后端发送数据,自动触发接收消息
        # print(group_device_name + '向服务器发来信息')
        message = json.loads(message['text'])
        if message['selectSend'] == 1:
            # 上报阿里物联网平台服务器(物模型)
            pus_ali.submit(message, self.channel_layer_manager)
        else:
            # 上报给设备
            sentDevice.submit(message, self.channel_layer_manager)

    def websocket_disconnect(self, message):
        group_device_name = self.scope['url_route']['kwargs']['group']
        # 客户端与服务端断开连接时，自动触发。
        print(group_device_name + '已断开连接')
        # 把客户端从组里删除掉
        async_to_sync(self.channel_layer_manager.channel_layer.group_discard)(
            group_device_name,
            self.channel_name
        )
        raise StopConsumer()

    # 自定义的发送数据给客户端, 需要在发送的地方来调用
    def send_data(self, event):
        # print('有要给客户端发送的数据event', event)
        text = event['message']
        self.send(text_data=text)


# """处理设备数据的WebSocket连接(折线图实时调用)"""
class DeviceDataConsumer(AsyncWebsocketConsumer):
    # 连接建立时执行
    async def connect(self):
        """从查询字符串获取参数"""
        query_string = self.scope.get('query_string', b'').decode()
        params = dict(pair.split('=') for pair in query_string.split('&') if '=' in pair)

        # 解析设备参数
        self.device_name = params.get('device_name', '')  # 设备名
        self.product_key = params.get('product_key', '')  # 设备key
        self.sendOrReceive = int(params.get('sendOrReceive', 0))  # 接收/发送
        self.Identifier = params.get('Identifier', '')  # 单个属性
        self.group_name = f'device_{self.device_name}'

        # 加入设备组
        await self.channel_layer.group_add(
            self.group_name,
            self.channel_name
        )
        await self.accept()

        # 发送初始数据
        await self.send_initial_data()

    # 发送初始数据
    async def send_initial_data(self):
        cache_key = f"device_{self.device_name}_data"
        version_key = f"device_{self.device_name}_version"

        # 1. 获取当前最新版本（优先数据库）
        current_version = await sync_to_async(
            lambda: DeviceData.objects
                    .filter(deviceName=self.device_name, productKey=self.product_key)
                    .order_by('-gmtCreate')
                    .values_list('version', flat=True)
                    .first() or 0)()

        # 2. 获取缓存数据和版本
        cached_data = await sync_to_async(cache.get)(cache_key)
        cached_version = await sync_to_async(cache.get)(version_key)

        # 3. 严格验证（缓存存在性检查）
        if (cached_data is not None and
                cached_version is not None and
                cached_version == current_version and
                current_version > 0):  # 排除零版本
            await self.send(json.dumps({
                'type': 'cached_data',
                'data': cached_data,
                'version': current_version
            }))
            return

        # 缓存失效时查询数据库
        new_data = await self.query_device_data()
        await sync_to_async(cache.set_many)({
            version_key: current_version,
            cache_key: new_data
        }, timeout=3600)

        await self.send(json.dumps({
            'type': 'initial_data',
            'data': new_data,
            'version': current_version
        }))

    # 异步查询设备数据
    async def query_device_data(self):
        @sync_to_async
        def _fetch_data():
            queryset = DeviceData.objects.filter(
                deviceName=self.device_name,
                productKey=self.product_key,
                sendOrReceive=self.sendOrReceive
            ).order_by('-gmtCreate')[:50]  # 限制50条最新数据

            result = defaultdict(list)

            for item in queryset:
                # 把属性标识转为属性名显示
                item_toDevice_id = item.toDevice_id
                item_device_id = Device.objects.filter(id=item_toDevice_id).first().toProduct_id

                item_data = json.loads(item.items) if item.items else {}
                if not item_data:
                    continue
                for device_type, data in item_data.items():
                    item_device_fun = FunDefine.objects.filter(toProductFuc_id=item_device_id, Identifier=device_type).first()
                    if item_device_fun:
                        attribute_name = item_device_fun.funcName
                    else:
                        attribute_name = device_type

                    if not data or not device_type:
                        continue
                    if (not self.Identifier) or (device_type == self.Identifier):
                        result[attribute_name].append({
                            'value': data['value'],
                            'time': data['time']
                        })
                # except json.JSONDecodeError:
                #     continue
            return dict(result)

        try:
            return await _fetch_data()
        except Exception as e:
            print(f"Database error: {e}")
            return defaultdict(list)

    # 接收信号推送
    async def device_update(self, event):  # 方法名必须与信号中的type对应
        """接收信号推送"""
        # print(f"收到推送数据: {event['data']}")
        new_data = await self.query_device_data()
        # await self.send(text_data=json.dumps(event['data']))
        await self.send(json.dumps({
            'type': event['data']['type'],       # 信号signals.py传来的
            'data': new_data,
            'version': event['data']['version']  # 信号signals.py传来的
        }))

    # 连接关闭时清理
    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.group_name,
            self.channel_name
        )

    # 前端重新请求时
    async def receive(self, text_data=None, bytes_data=None):
        try:
            data = json.loads(text_data)
            # 处理过滤请求
            if data.get('action') == 'filter_data':
                params = data.get('payload', {})
                # print('到这里了params:', params)
                self.device_name = params.get('device_name', '')  # 设备名
                self.product_key = params.get('product_key', '')  # 设备key
                self.sendOrReceive = int(params.get('sendOrReceive', 0))  # 接收/发送
                self.Identifier = params.get('Identifier', '')  # 单个属性
                # filtered_data = await self._get_filtered_data(
                #     params.get('sendOrReceive', 0),
                #     params.get('Identifier', '')
                # )
                new_data = await self.query_device_data()
                await self.send(text_data=json.dumps({
                    'type': 'filtered_data',
                    'data': new_data
                }))

        except Exception as e:
            print(f"处理前端请求失败: {e}")

    # 按条件查询数据
    # async def _get_filtered_data(self, sendOrReceive, Identifier):
    #     @sync_to_async
    #     def _query():
    #         queryset = DeviceData.objects.filter(
    #             deviceName=self.device_name,
    #             productKey=self.product_key,
    #             sendOrReceive=sendOrReceive
    #         ).order_by('-gmtCreate')[:50]
    #
    #         result = {}
    #         for item in queryset:
    #             item_data = json.loads(item.items)
    #             for k, v in item_data.items():
    #                 if not Identifier or k == Identifier:
    #                     result.setdefault(k, []).append({
    #                         'value': v['value'],
    #                         'time': v['time']
    #                     })
    #         return result
    #
    #     return await _query()
